package com.echat.messagesender.sender.util;

import com.echat.messagesender.sender.constant.Constants;
import org.apache.http.*;
import org.apache.http.client.HttpRequestRetryHandler;
import org.apache.http.client.config.RequestConfig;
import org.apache.http.client.entity.UrlEncodedFormEntity;
import org.apache.http.client.methods.CloseableHttpResponse;
import org.apache.http.client.methods.HttpGet;
import org.apache.http.client.methods.HttpPost;
import org.apache.http.client.methods.HttpRequestBase;
import org.apache.http.client.protocol.HttpClientContext;
import org.apache.http.config.*;
import org.apache.http.conn.ConnectTimeoutException;
import org.apache.http.conn.ConnectionKeepAliveStrategy;
import org.apache.http.conn.socket.ConnectionSocketFactory;
import org.apache.http.conn.socket.PlainConnectionSocketFactory;
import org.apache.http.conn.ssl.NoopHostnameVerifier;
import org.apache.http.conn.ssl.SSLConnectionSocketFactory;
import org.apache.http.entity.StringEntity;
import org.apache.http.entity.mime.MultipartEntityBuilder;
import org.apache.http.impl.client.CloseableHttpClient;
import org.apache.http.impl.client.HttpClients;
import org.apache.http.impl.conn.PoolingHttpClientConnectionManager;
import org.apache.http.message.BasicHeaderElementIterator;
import org.apache.http.message.BasicNameValuePair;
import org.apache.http.protocol.HTTP;
import org.apache.http.protocol.HttpContext;

import javax.net.ssl.SSLContext;
import javax.net.ssl.SSLException;
import javax.net.ssl.TrustManager;
import javax.net.ssl.X509TrustManager;
import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.io.InterruptedIOException;
import java.net.UnknownHostException;
import java.nio.charset.CodingErrorAction;
import java.security.KeyManagementException;
import java.security.NoSuchAlgorithmException;
import java.security.cert.CertificateException;
import java.security.cert.X509Certificate;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
import java.util.Properties;
import java.util.concurrent.TimeUnit;

/**
 * Created by wangyitao on 2016/8/3.
 * http连接池管理类，可以同时执行多个线程get、post请求并获取对应的内容
 */
public class HttpUtil {

    private static CloseableHttpClient httpclient = null;
    private static RequestConfig config = null;

    static {

        TrustManager trustManager = new X509TrustManager() {
            @Override
            public void checkClientTrusted(X509Certificate[] x509Certificates, String s) throws CertificateException {

            }

            @Override
            public void checkServerTrusted(X509Certificate[] x509Certificates, String s) throws CertificateException {

            }

            @Override
            public X509Certificate[] getAcceptedIssuers() {
                return null;
            }
        };

        SSLContext sslContext = null;
        try {
            sslContext = SSLContext.getInstance("TLS");
            sslContext.init(null, new TrustManager[]{trustManager}, null);
        } catch (NoSuchAlgorithmException e) {
            e.printStackTrace();
        } catch (KeyManagementException e) {
            e.printStackTrace();
        }

        SSLConnectionSocketFactory sslConnectionSocketFactory = new SSLConnectionSocketFactory(sslContext, NoopHostnameVerifier.INSTANCE);

        //集成连接管理器
        Registry<ConnectionSocketFactory> registry = RegistryBuilder.<ConnectionSocketFactory>create()
                .register("http", PlainConnectionSocketFactory.INSTANCE)
                .register("https", sslConnectionSocketFactory)
                .build();

        //连接池管理器
        PoolingHttpClientConnectionManager connectionManager = new PoolingHttpClientConnectionManager(registry);

        // Create socket configuration
        SocketConfig socketConfig = SocketConfig.custom().setSoKeepAlive(true).setTcpNoDelay(true).build();
        connectionManager.setDefaultSocketConfig(socketConfig);
        // Create message constraints
        MessageConstraints messageConstraints = MessageConstraints.custom()
                .setMaxHeaderCount(200)
                .setMaxLineLength(2000)
                .build();
        // Create connection configuration
        ConnectionConfig connectionConfig = ConnectionConfig.custom()
                .setMalformedInputAction(CodingErrorAction.IGNORE)
                .setUnmappableInputAction(CodingErrorAction.IGNORE)
                .setCharset(Consts.UTF_8)
                .setMessageConstraints(messageConstraints)
                .setBufferSize(4128)
                .build();
        connectionManager.setDefaultConnectionConfig(connectionConfig);
        connectionManager.setMaxTotal(200);
        connectionManager.setDefaultMaxPerRoute(20);
        connectionManager.closeExpiredConnections();
        connectionManager.closeIdleConnections(30, TimeUnit.SECONDS);

        //请求重试处理，返回true重试，false放弃
        HttpRequestRetryHandler retryHandler = new HttpRequestRetryHandler() {
            public boolean retryRequest(IOException exception, int executionCount, HttpContext context) {
                // 最大重试次数
                if (executionCount >= 5) {
                    return false;
                }
                // 超时
                if (exception instanceof InterruptedIOException) {
                    return true;
                }
                // 目标服务器不可达
                if (exception instanceof UnknownHostException) {
                    return false;
                }
                // 连接被拒绝
                if (exception instanceof ConnectTimeoutException) {
                    return false;
                }
                // ssl握手异常
                if (exception instanceof SSLException) {
                    return false;
                }
                HttpClientContext clientContext = HttpClientContext.adapt(context);
                HttpRequest request = clientContext.getRequest();
                // 如果请求是幂等的，就再次尝试
                boolean idempotent = !(request instanceof HttpEntityEnclosingRequest);
                if (idempotent) {
                    return true;
                }
                return false;
            }
        };

        ConnectionKeepAliveStrategy keepAliveStrategy = new ConnectionKeepAliveStrategy() {

            public long getKeepAliveDuration(HttpResponse response, HttpContext context) {
                // Honor 'keep-alive' header
                HeaderElementIterator it = new BasicHeaderElementIterator(
                        response.headerIterator(HTTP.CONN_KEEP_ALIVE));
                while (it.hasNext()) {
                    HeaderElement he = it.nextElement();
                    String param = he.getName();
                    String value = he.getValue();
                    if (value != null && param.equalsIgnoreCase("timeout")) {
                        try {
                            return Long.parseLong(value) * 1000;
                        } catch (NumberFormatException ignore) {

                        }
                    }
                }
                HttpHost target = (HttpHost) context.getAttribute(HttpClientContext.HTTP_TARGET_HOST);
                // keep alive for 30 seconds
                return 30 * 1000;
            }
        };

        //创建自定义的httpclient实例
        httpclient = HttpClients.custom()
                .setConnectionManager(connectionManager)
                .setRetryHandler(retryHandler)
                .setKeepAliveStrategy(keepAliveStrategy)
                .build();

        //配置请求的超时设置
        config = RequestConfig.custom()
                .setConnectTimeout(5000)//设置连接超时时间，单位毫秒
                .setConnectionRequestTimeout(5000)//设置从connect Manager获取Connection 超时时间，单位毫秒：这个属性是新加的属性，因为目前版本是可以共享连接池的
                .setSocketTimeout(5000)//请求获取数据的超时时间，单位毫秒：如果访问一个接口，多少时间内无法返回数据，就直接放弃此次调用
                .build();
    }

    /**
     * get请求
     *
     * @param url
     * @return String
     * @throws Exception
     */
    public static String get(String url) throws Exception {
        HttpGet httpGet = new HttpGet(url);
        httpGet.setConfig(config);
        CloseableHttpResponse response = httpclient.execute(httpGet);
        return getResponseString(httpGet, response);
    }


    /**
     * post请求
     *
     * @param url
     * @param parameters
     * @return
     * @throws Exception
     */
    public static String post(String url, Properties parameters) throws Exception {
        HttpPost httpPost = new HttpPost(url);
        if (parameters != null && parameters.size() > 0) {
            List<NameValuePair> formparams = new ArrayList<NameValuePair>();
            Iterator it = parameters.keySet().iterator();
            while (it.hasNext()) {
                String name = (String) it.next();
                String value = parameters.getProperty(name);
                formparams.add(new BasicNameValuePair(name, value));
            }
            UrlEncodedFormEntity postentity = new UrlEncodedFormEntity(formparams, Constants.DEFAULT_CHARSET);
            httpPost.setEntity(postentity);
            httpPost.setConfig(config);
        }
        CloseableHttpResponse response = httpclient.execute(httpPost);
        return getResponseString(httpPost, response);
    }

    /**
     * post数据流
     *
     * @param url
     * @param data
     * @return
     * @throws Exception
     */
    public static String postData(String url, String data) throws Exception {
        HttpPost httpPost = new HttpPost(url);
        StringEntity entity = new StringEntity(data, Constants.DEFAULT_CHARSET);
        httpPost.setEntity(entity);
        httpPost.setConfig(config);
        CloseableHttpResponse response = httpclient.execute(httpPost);
        return getResponseString(httpPost, response);
    }

    /**
     * post数据流
     *
     * @param url
     * @param data
     * @return
     * @throws Exception
     */
    public static byte[] postDataBuffer(String url, String data) throws Exception {
        HttpPost httpPost = new HttpPost(url);
        StringEntity entity = new StringEntity(data, Constants.DEFAULT_CHARSET);
        httpPost.setEntity(entity);
        httpPost.setConfig(config);
        CloseableHttpResponse response = httpclient.execute(httpPost);
        return getResponseBuffer(httpPost, response);
    }

    /**
     * post数据流
     *
     * @param httpPost
     * @param data
     * @return
     * @throws Exception
     */
    public static String postData(HttpPost httpPost, String data) throws Exception {
        StringEntity entity = new StringEntity(data, Constants.DEFAULT_CHARSET);
        httpPost.setEntity(entity);
        httpPost.setConfig(config);
        CloseableHttpResponse response = httpclient.execute(httpPost);
        return getResponseString(httpPost, response);
    }

    /**
     * Post文件流，即：上传文件
     *
     * @param url
     * @param formName
     * @param file
     * @return
     * @throws Exception
     */
    public static String postFile(String url, String formName, File file) throws Exception {
        HttpPost httpPost = new HttpPost(url);
        MultipartEntityBuilder builder = MultipartEntityBuilder.create();
        builder.addBinaryBody(formName, file);
        httpPost.setEntity(builder.build());
        httpPost.setConfig(config);
        CloseableHttpResponse response = httpclient.execute(httpPost);
        return getResponseString(httpPost, response);
    }

    /**
     * 通过response获取相应流信息
     *
     * @param request
     * @param response
     * @return
     * @throws IOException
     */
    private static byte[] getResponseBuffer(HttpRequestBase request, CloseableHttpResponse response) throws IOException {
        byte[] buffer = null;
        try {
            HttpEntity entity = response.getEntity();
            if (entity != null) {
                InputStream inputStream = entity.getContent();
                buffer = IOUtil.toByteArray(inputStream);
            }
            request.abort();
        } finally {
            response.close();
        }
        return buffer;
    }

    /**
     * 通过response获取相应
     *
     * @param request
     * @param response
     * @return
     * @throws IOException
     */
    private static String getResponseString(HttpRequestBase request, CloseableHttpResponse response) throws IOException {
        String result = null;
        try {
            HttpEntity entity = response.getEntity();
            if (entity != null) {
                InputStream inputStream = entity.getContent();
                result = IOUtil.inputStream2String(inputStream);
            }
            request.abort();
        } finally {
            response.close();
        }
        return result;
    }


}
